﻿using Daemon.Common.Auth;
using Daemon.Data.Infrastructure.Auth;
using Daemon.Model;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Authorization.Policy;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Controllers;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;

namespace Daemon.Common.Handlers
{
    public class CustomAuthorizationMiddlewareResultHandler : IAuthorizationMiddlewareResultHandler
    {
        private readonly IAuthorizationMiddlewareResultHandler _handler;

        private readonly ClientAuthCache _clientAuthCache;

        private readonly ApiDBContent _dbContext;

        public CustomAuthorizationMiddlewareResultHandler(ClientAuthCache clientAuthCache,
        ApiDBContent dbContext)
        {
            _handler = new AuthorizationMiddlewareResultHandler();
            _clientAuthCache = clientAuthCache;
            _dbContext = dbContext;
        }

        public async Task HandleAsync(RequestDelegate next, HttpContext context, AuthorizationPolicy policy, PolicyAuthorizationResult authorizeResult)
        {
            if (!CheckUserPermission(authorizeResult, context))
            {
                return;
            }

            if (!authorizeResult.Succeeded)
            {
                await context.Response.WriteAsync(JsonConvert.SerializeObject(new ResultModel(HttpStatusCode.Unauthorized, "")).ToLower());
                return;
            }

            var claims = context.Request.HttpContext.User.Claims.ToList();
            if (claims.Any())
            {
                var userId = claims.First(r => r.Type == "Id")?.Value;
                var prefix = claims.First(r => r.Type == "Prefix")?.Value;
                var userName = claims.First(r => r.Type == "Name")?.Value;
                if (string.IsNullOrEmpty(userId))
                {
                    await context.Response.WriteAsync(JsonConvert.SerializeObject(new ResultModel(HttpStatusCode.Unauthorized, "")).ToLower());
                    return;
                }

                IAuthInfo authInfo = _clientAuthCache.GetAuthInfoByClientUser(prefix + "_" + userName);
                BaseAuthUser userEntity = null;
                if (authInfo != null)
                {
                    userEntity = authInfo.UserEntity;
                }

                if (userEntity == null)
                {
                    try
                    {
                        RefreshUserCache(_dbContext, userId, userName, prefix, ref userEntity);
                    }
                    catch (Exception ex)
                    {

                        await context.Response.WriteAsync(JsonConvert.SerializeObject(new ResultModel(HttpStatusCode.Unauthorized, "")).ToLower());
                        return;
                    }
                }

                if (userEntity == null)
                {
                    await context.Response.WriteAsync(JsonConvert.SerializeObject(new ResultModel(HttpStatusCode.Unauthorized, "")).ToLower());
                    return;
                }
            }

            await _handler.HandleAsync(next, context, policy, authorizeResult);
        }

        private bool CheckUserPermission(PolicyAuthorizationResult authorizeResult, HttpContext context)
        {
            if (authorizeResult.Forbidden && authorizeResult.AuthorizationFailure != null)
            {
                if (authorizeResult.AuthorizationFailure.FailedRequirements.Any(requirement => requirement is PermissionRequirement))
                {
                    context.Response.StatusCode = (int)HttpStatusCode.NotFound;
                    return false;
                }

                // Other transformations here 
            }

            return true;
        }

        private bool SkipAuthorization(HttpContext context)
        {
            var endPoint = context.GetEndpoint();
            var controllerActionDescriptor = (ControllerActionDescriptor)endPoint.Metadata.AsEnumerable().FirstOrDefault(d => d is ControllerActionDescriptor);
            var attributeList = controllerActionDescriptor?.MethodInfo.GetCustomAttributes(true);
            var controllerAuthorityAttribute = (AllowAnonymousAttribute)attributeList.Where(a => a.GetType().Name == typeof(Daemon.Common.Attribute.AllowAnonymousAttribute).Name).ToList().FirstOrDefault();
            if (controllerAuthorityAttribute != null)
            {
                return true;
            }

            return false;
        }

        private void RefreshUserCache(ApiDBContent context, string userId, string userName, string prefix, ref BaseAuthUser userEntity)
        {
            var user = context.AdminUser.Where(r => r.Id == Convert.ToInt32(userId)).Select(r => new BaseAuthUser() { Id = r.Id, UserType = "WX", UserName = r.UserName }).ToList()[0];
            var authInfo = new AuthInfoEF(user);
            userEntity = authInfo.UserEntity;
            _clientAuthCache.AddClientUserEntryAsync(prefix + "_" + userName, authInfo);
        }
    }
}
